#define GAME_CPP
#include <windows.h>											// Header File For Windows
#include <stdio.h>												// Header File For Standard Input / Output
#include <stdarg.h>												// Header File For Variable Argument Routines
#include <math.h>	
#include "openglframework.h"
#include "Camera.h"
#include <gl\gl.h>												// Header File For The OpenGL32 Library
#include <gl\glu.h>												// Header File For The GLu32 Library
#include "openGLStuff.h"
#include "game.h"
#include "InputManager.h"
#include "MapManager.h"
#include <btBulletDynamicsCommon.h>
#include "glut.h"
#include "random.h"
#include "SoundManager.h"
#include "..\soil\SOIL.h"

// Toggle MPF Output
//#define DEBUG_MPF

// Choose camera mode
#define GAME_CAMERA
//#define DEBUG_CAMERA

// Declarations
const char8_t CGame::mGameTitle[]="Impossible3D";
CGame* CGame::sInstance=NULL;
BOOL Initialize (GL_Window* window, Keys* keys)					// Any OpenGL Initialization Goes Here
{
	initOpenGLDrawing(window,keys,0.0f, 1.0f, 0.0f);
	CGame::CreateInstance();
	CGame::GetInstance()->init();
	
	return TRUE;						
}

void CGame::init()
{
	// Init input manager singleton
	InputManagerC::CreateInstance();
	InputManagerC::GetInstance()->init();

	// Init subtitle Manager 
	m_subtitleManager = SubtitleManager::CreateInstance();
	m_subtitleManager->init();

	// Init SoundManager
	SoundManager::CreateInstance();
	SoundManager::GetInstance()->init();

	//playSound
	SoundManager::GetInstance()->PlayBackgoundSound(SN_BACKGROUND_LEVEL1);

	// Init the mapLevel1(may move to elsewhere
	MapManager::CreateInstance();
	MapManager::GetInstance()->init();
	MapManager::GetInstance()->parseMapFile("mapFile/level1.txt");
	list<trackInfoT>* myMapInfoList = MapManager::GetInstance()->getMapList();

	// Init physics system
	m_physicsHandle.Init();
	// Init lighting
	m_lightManager.Init();

	//generate the cube according to the mapList
	m_MapObjList = new list<BaseShape*>();
	list<trackInfoT>::iterator it;
	for(it = myMapInfoList->begin(); it!=myMapInfoList->end(); it++)
	{
		//0 for cube, 1 for pyramid, hardcode it , fix later
		BaseShape* _shape;
		switch(it->trackType)
		{
			case 0:
				_shape = new Cube();
				m_MapObjList->push_back(_shape);
				m_physicsHandle.AddBoxEnvironment(_shape->GetPhysicsEntity(), it->posX, it->posY, it->posZ, 0.5f*it->scaleX, 0.5f*it->scaleY, 0.5f*it->scaleZ, 0.0f);
				break;
			case 1:
				_shape = new Pyramid();
				m_MapObjList->push_back(_shape);
				m_physicsHandle.AddConeEnvironment(_shape->GetPhysicsEntity(), it->posX, it->posY, it->posZ, 0.5f * sqrt(0.7 * (it->scaleX) * (it->scaleZ)), 1.0f*(it->scaleY), 0.0f);
				break;
		}
	}

	// Add player rigid body
	m_physicsHandle.AddBoxPlayer((m_player.GetPhysicsEntity()), 0, 5.0f, -2.0f, 0.5f, 0.5f, 0.5f, 2.0f);
	m_player.ResetPlayer();

	// Set handle to player's collision object
	ImpossiblePhysics::g_playerCollider = m_player.GetPhysicsEntity();

	// Set game dimension
	m_player.SetMode3D(g_bMode3D);
	
	// Start game with start screen showing
	m_bShowStartScreen = true;
	m_bShowEndScreen   = false;
}

void CGame::UpdateCamera(float_t argDeltaTime)
{

#ifdef DEBUG_CAMERA

	// Update camera based on input
	if(InputManagerC::GetInstance()->IsMovingForward()) 
	{
		m_camera.MoveForward(argDeltaTime);
	}

	if(InputManagerC::GetInstance()->IsMovingBackward())
	{
		m_camera.MoveBackward(argDeltaTime);
	}

	if(InputManagerC::GetInstance()->IsMovingRight())
	{
		m_camera.MoveRight(argDeltaTime);
	}

	if(InputManagerC::GetInstance()->IsMovingLeft())
	{
		m_camera.MoveLeft(argDeltaTime);
	}

	if(InputManagerC::GetInstance()->IsMovingUp())
	{
		m_camera.MoveUp(argDeltaTime);
	}

	if(InputManagerC::GetInstance()->IsMovingDown())
	{
		m_camera.MoveDown(argDeltaTime);
	}

	if(InputManagerC::GetInstance()->IsRotatingYawCC())
	{
		m_camera.RotateCameraYawCC(argDeltaTime);
	}

	if(InputManagerC::GetInstance()->IsRotatingYawCW())
	{
		m_camera.RotateCameraYawCW(argDeltaTime);
	}

#endif
}

// Update scene
void CGame::UpdateFrame(float_t argDeltaTime)			
{
	if(m_bShowStartScreen)
	{
		if(InputManagerC::IsEnterPressed())
		{
			m_bShowStartScreen = false;
			m_screenManager.Unload();
		}
	}
	else
	{
		// Check for view toggle
		if(InputManagerC::IsToggle())
		{
			g_bMode3D = !g_bMode3D;
			m_player.SetMode3D(g_bMode3D);
		}

#ifdef DEBUG_MPF
		char _debugString[100];
		sprintf(_debugString, "MPF:%f\n", argDeltaTime);
		OutputDebugStringA(_debugString);
#endif
		//	// Update objects
#ifdef GAME_CAMERA
		m_player.Update(argDeltaTime);
#endif

		list<BaseShape*>::iterator it;
		for(it = m_MapObjList->begin(); it!=m_MapObjList->end(); it++)
		{
			(*it)->Update(argDeltaTime);
		}

		// Update physics system
		m_physicsHandle.Update(argDeltaTime);

		// Update input manager
		InputManagerC::GetInstance()->update(argDeltaTime);

		UpdateCamera(argDeltaTime);

		m_subtitleManager->Update(argDeltaTime);

		//randomly generate subtitle
		if(getRangedRandom(0.0f, 100.0f) > 99.5f)
		{
			m_subtitleManager->AddDynamicSubtitle();
		}

	}
	if(m_player.GetCacheX() <= -1905 && !m_bShowEndScreen)
	{
		m_player.SetWinState();
		m_bShowEndScreen = true;
	}
}

// Draw scene
void CGame::DrawScene(void)										
{
	// Clear buffers
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	if(m_bShowStartScreen)
	{
		m_screenManager.DrawStartScreen();
	}
	else
	{
		if(g_bMode3D)
		{
			// Set view matrix
	#ifdef DEBUG_CAMERA
			m_camera.SetLookAt();
	#endif

	#ifdef GAME_CAMERA
			m_player.SetLookAt();
	#endif
		}

		if(g_bMode3D)
		{
			// Draw player
			m_player.Draw();

			list<BaseShape*>::iterator it;
			for(it = m_MapObjList->begin(); it!=m_MapObjList->end(); it++)
			{
				(*it)->Draw();
			}
		}
		else
		{
			list<BaseShape*>::iterator it;
			for(it = m_MapObjList->begin(); it!=m_MapObjList->end(); it++)
			{
				(*it)->Draw2D();
			}

			m_player.Draw2D();
		}
	}

	m_subtitleManager->Draw();
}

void CGame::displayText( float x, float y, int r, int g, int b, const char *string )
{
	//glPushMatrix();
	//glMatrixMode(GL_MODELVIEW);
	//glLoadIdentity();
	glMatrixMode(GL_PROJECTION);
	glPushMatrix();
	glLoadIdentity();

	//glOrtho(0.0f, 1024.0f, 768.0f, 0.0f, 0.0f, 1.0f);
	glOrtho(-1000.0, 1000.0, -1000.0, 1000.0, -400.0, 400.0);

	int j = strlen( string );

	glColor3f( r, g, b );
	glRasterPos2f(x, y);
	glScalef(0.5f, 0.5f, 1.0f);
	//glRasterPos3f( 30 ,20 ,-1.0f );
	glLineWidth(3.0f);
	char *c;
	c = (char*)malloc(sizeof(strlen(string)+1));
	strcpy(c, string);
	for (; *c != '\0'; c++)
	{
		glutStrokeCharacter(GLUT_STROKE_ROMAN  , *c);
	}

	//gluPerspective(45.0f,(GLfloat)1024/(GLfloat)768,0.1f,100.0f);
	glPopMatrix();
	glMatrixMode (GL_MODELVIEW);
	//glPopMatrix();
}

CGame *CGame::CreateInstance()
{
	sInstance = new CGame();
	return sInstance;
}
void CGame::shutdown()
{
	//BallManagerC::GetInstance()->shutdown();
}
void CGame::DestroyGame(void)
{
/*	delete BallManagerC::GetInstance();*/
}

CGame::~CGame()
{


};